﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;

namespace GpsInfoNS
{
    // $GPGGA,032524.00,2303.00591,N,11324.42138,E,2,12,0.78,13.2,M,-5.1,M,,0000*7E  0x0d 0x0a
    public struct Gpgga
    {
        public string _utcHour;
        public string _utcMinute;
        public string _utcSecond;
        public string _utcMS;
        public string _latitude;
        public string _latitudeDirection;
        public string _longitude;
        public string _longitudeDirection;
        public string _gpsQualityIndicator;
        public string _usedSatellites;
        public string _hdop;//水平衰减因子
        public string _seaLevel;
        public string _seaLevelUnit;
        public string _geoidalSeparation;
        public string _geoidalSeparationUnit;
        public string _ageOfDiffData;
        public string _refStationID;
    }

    public struct Gprmc
    {
        public string _utcHour;
        public string _utcMinute;
        public string _utcSecond;
        public string _utcMS;
        public string _status;
        public string _latitude;
        public string _latitudeDirection;
        public string _longitude;
        public string _longitudeDirection;
        public string _speed;
        public string _cog;//course of ground
        public string _day;
        public string _month;
        public string _year;
        public string _mv;//magnetic variation
        public string _mvd;//magnetic variation degree
        public string _mode;//nmea 2.3 above
        public string _navStatus;//nmea 4.1 above
    }

    public class GpsInfo
    {
        static Queue<string> _queInput;
        static Mutex _mutex;
        static Gpgga _gpgga;
        static Gprmc _gprmc;

        Thread _parseGgaThrd;

        static StringBuilder _strBuilder;

        public void Quit()
        {
            _parseGgaThrd.Abort();
        }

        public GpsInfo()
        {
            _strBuilder = new StringBuilder();

            _mutex = new Mutex();
            _queInput = new Queue<string>();

            _gpgga._utcHour = "-";
            _gpgga._utcMinute = "-";
            _gpgga._utcSecond = "-";
            _gpgga._latitude = "-";
            _gpgga._latitudeDirection = "-";
            _gpgga._longitude = "-";
            _gpgga._longitudeDirection = "-";

            _gpgga._gpsQualityIndicator = "-";
            _gpgga._usedSatellites = "-";
            _gpgga._seaLevel = "-";
            _gpgga._seaLevelUnit = "-";

            _parseGgaThrd = new Thread(new ParameterizedThreadStart(parseGpsInfo));
            _parseGgaThrd.Start();

        }


        static void parseGpsInfo(object obj)
        {
            bool ggaIsMatch = false;
            bool rmcIsMatch = false;

            string input;
            while (true)
            {
                try
                {
                    _mutex.WaitOne();
                    input = _queInput.Dequeue();
                    _mutex.ReleaseMutex();
                }

                catch (Exception)
                {
                    _mutex.ReleaseMutex();
                    Thread.Sleep(10);
                    continue;
                }
                

                if ((null == input) || ("" == input))
                {
                    Thread.Sleep(10);
                }
                else
                {
                    try
                    {
                        _strBuilder.Append(input);

                        string concatenatedInputs = _strBuilder.ToString();
                        
                        //try to find GGA or RMC.
                        ggaIsMatch = System.Text.RegularExpressions.Regex.IsMatch(concatenatedInputs, @"\$[A-Z][A-Z]GGA.+?\r\n");
                        rmcIsMatch = System.Text.RegularExpressions.Regex.IsMatch(concatenatedInputs, @"\$[A-Z][A-Z]RMC.+?\r\n");

                        if (true == rmcIsMatch)
                        {
                            parseRmc(concatenatedInputs);
                        }
                        
                        if (true == ggaIsMatch)
                        {
                            parseGga(concatenatedInputs);
                        }

                        if ((false == rmcIsMatch) && (false == ggaIsMatch))
                        {
                            //Do NOT clear strBuilder.
                            //Because no enough input string to parse gga or rmc.
                            continue;
                        }
                        else if ((true == rmcIsMatch) && (true == ggaIsMatch))
                        {
                            //Clear the strBuilder.
                            //Because already parse both rmc and gga ok.
                            _strBuilder.Clear();
                        }
                        else
                        {
                            //do nothing.
                        }
                    }

                    catch (Exception)
                    {
                        //do nothing.
                    }
                }
            }
        }

        /// $GNRMC,065408.000,A,3033.3700,N,10403.8695,E,3.90,276.73,241117,,,A*71
        private static void parseRmc(string input)
        {
            Match match = System.Text.RegularExpressions.Regex.Match(input, @"\$[A-Z][A-Z]RMC.+?\r\n");

            input = match.Value.ToString();

            string[] strArray = input.Split(',');
            //strArray[0] XXRMC

            Regex twoDigitalReg = new Regex(@"(\d\d)(\d\d)(\d\d)");
            if (strArray[1] != "")
            {
                string utc = strArray[1].ToString();
                match = twoDigitalReg.Match(utc);
                _gprmc._utcHour = match.Groups[1].Value.ToString();
                _gprmc._utcMinute = match.Groups[2].Value.ToString();
                _gprmc._utcSecond = match.Groups[3].Value.ToString();
            }
            
            _gprmc._status = strArray[2];
            _gprmc._latitude = strArray[3];
            _gprmc._latitudeDirection = strArray[4];
            _gprmc._longitude = strArray[5];
            _gprmc._longitudeDirection = strArray[6];

            if (strArray[7] != "")
            {
                try
                {
                    double speed = (Convert.ToDouble(strArray[7]) * 1.852);
                    _gprmc._speed = speed.ToString();
                }
                catch (Exception)
                {

                    _gprmc._speed = "";
                }
                
            }

            _gprmc._cog = strArray[8];

            match = twoDigitalReg.Match(strArray[9]);
            _gprmc._day = match.Groups[1].Value.ToString();
            _gprmc._month = match.Groups[2].Value.ToString();
            _gprmc._year = match.Groups[3].Value.ToString();

            _gprmc._mv = strArray[10];
            _gprmc._mvd = strArray[11];

            return ;
        } 
        

        private static string parseGga(string gga)
        {
            Match match = System.Text.RegularExpressions.Regex.Match(gga, @"\$[A-Z][A-Z]GGA.+?\r\n");

            gga = match.Value.ToString();

            string[] strArray = gga.Split(',');
            //strArray[0] XXGGA

            if (strArray[1] != "")
            {
                Regex utctimeReg = new Regex(@"(\d\d)(\d\d)(\d\d)");
                string utc = strArray[1].ToString();
                match = utctimeReg.Match(utc);
                _gpgga._utcHour = match.Groups[1].Value.ToString();
                _gpgga._utcMinute = match.Groups[2].Value.ToString();
                _gpgga._utcSecond = match.Groups[3].Value.ToString();
            }

            _gpgga._latitude = strArray[2];
            _gpgga._latitudeDirection = strArray[3];
            _gpgga._longitude = strArray[4];
            _gpgga._longitudeDirection = strArray[5];
            _gpgga._gpsQualityIndicator = strArray[6];
            _gpgga._usedSatellites = strArray[7];
            _gpgga._hdop = strArray[8];
            _gpgga._seaLevel = strArray[9];
            _gpgga._seaLevelUnit = strArray[10];
            _gpgga._geoidalSeparation = strArray[11];
            _gpgga._geoidalSeparationUnit = strArray[12];
            _gpgga._ageOfDiffData = strArray[13];
            _gpgga._refStationID = strArray[14];
            return gga;
        }
        
        public void SaveData(byte [] data)
        {
            try
            {
                string input = new ASCIIEncoding().GetString(data);
                if (("" == input) || (null == input))
                {
                    return;
                }

                _mutex.WaitOne();
                _queInput.Enqueue(input);
                _mutex.ReleaseMutex();
            }

            catch (Exception)
            {
                //do nothing.
            }

            return;
        }

        public Gpgga GetGpgga()
        {
            return _gpgga;
        }

        public Gprmc GetGprmc()
        {
            return _gprmc;
        }
    }
}
